/**
 * 
 */
package com.google.code.cisx.wfe.model;

import java.util.ArrayList;
import java.util.List;

import com.google.code.cisx.wfe.api.WorkflowException;

/**
 * A Transition is synonymous with a Task.
 * <p>
 * A Transition <code><b>'t'</b></code> is said to be enabled if and only if
 * each input {@link Place} of 't' contains at least one token.
 * <p>
 * A Transition can therefore be defined by the number of {@link Place}s it is
 * composed of.
 * <p>
 * An enabled {@link Transition} may fire. If a {@link Transition} fires, then
 * it consumes one {@link Token} from each input {@link Place}, and produces one
 * Token in each output {@link Place}.
 * 
 * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
 * @since 1.0
 * @version $Id$
 */
public class Transition<D> extends AbstractPetriNetEntity<D> {

    /**
     * {@link InputArc}s that to this Transition.
     */
    private List<InputArc<?>> inputs;

    /**
     * {@link OutputArc}s from this Transition.
     */
    private List<OutputArc<?>> outputs;

    /**
     * {@link TransitionProcessor} that does the grunt work when this Transition
     * gets fired.
     */
    private TransitionProcessor processor;

    /**
     * 
     * @param label
     */
    public Transition(String label) {
        super(label);
    }

    /**
     * 
     * @param i
     */
    public void addInput(InputArc<?> i) {
        if (null == inputs)
            this.inputs = new ArrayList<InputArc<?>>();
        this.inputs.add(i);
    }

    /**
     * 
     * @param o
     */
    public void addOutput(OutputArc<?> o) {
        if (null == outputs)
            this.outputs = new ArrayList<OutputArc<?>>();
        this.outputs.add(o);
    }

    /**
     * Removes the specified {@link InputArc} connection to this Transition.
     * 
     * @param i
     */
    public void removeInput(InputArc<?> i) {
        if (null != inputs && this.inputs.contains(i))
            this.inputs.remove(i);
    }

    /**
     * Removes the specified {@link OutputArc} connection from this Transition.
     * 
     * @param o
     */
    public void removeInput(OutputArc<?> o) {
        if (null != outputs && this.outputs.contains(o))
            this.outputs.remove(o);
    }

    /**
     * @return the inputs
     */
    public List<InputArc<?>> getInputs() {
        return inputs;
    }

    /**
     * @return the outputs
     */
    public List<OutputArc<?>> getOutputs() {
        return outputs;
    }

    /**
     * Determine if there is atleast one Token on each of the {@link Place}s
     * that define this Transition.
     * 
     * @return
     */
    public boolean isEnabled() {
        // Determine if there is atleast one Token on
        // each of the input Places that define this Transition.
        for (InputArc<?> i : this.inputs)
            if (i.getSource().getTokens().size() <= 0)
                return false;
        return true;
    }

    /**
     * {@link TransitionProcessor} that executes when this Transition gets
     * fired.
     * 
     * @param processor
     */
    public void setTransitionProcessor(TransitionProcessor processor) {
        this.processor = processor;
    }

    /**
     * @return the processor
     */
    public TransitionProcessor getTransitionProcessor() {
        return processor;
    }

    /**
     * {@link TransitionProcessor} is supposed to be used as an anonymous class.
     * 
     * @author <a href='mailto:rthakur@bravurasolutions.com'>Rahul Thakur</a>
     * @since 1.0
     * @version $Id$
     */
    public class TransitionProcessor implements
            PetriNetEntityProcessor<Transition<D>> {

        @Override
        public void process(Transition<D> entity) throws WorkflowException {
            // TODO Auto-generated method stub
            System.out.println("Processing a Transition with data: "
                    + entity.getData());
        }

    }

}
